package com.zmm.xiaohashu.auth.runner;

import cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.zmm.framework.common.utils.JsonUtils;
import com.zmm.xiaohashu.auth.constant.RedisKeyConstants;
import com.zmm.xiaohashu.auth.domain.dataobject.PermissionDO;
import com.zmm.xiaohashu.auth.domain.dataobject.RoleDO;
import com.zmm.xiaohashu.auth.domain.dataobject.RolePermissionDO;
import com.zmm.xiaohashu.auth.domain.mapper.PermissionDOMapper;
import com.zmm.xiaohashu.auth.domain.mapper.RoleDOMapper;
import com.zmm.xiaohashu.auth.domain.mapper.RolePermissionDOMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author: 福禄寿
 * @Date: 2024/9/11 下午4:42
 * @Des: xiaohashu
 **/
@Component
@Slf4j
public class PushRolePermissions2RedisRunner implements ApplicationRunner {

    @Resource
    private RedisTemplate<String, String> redisTemplate;
    @Resource
    private RoleDOMapper roleDOMapper;
    @Resource
    private PermissionDOMapper permissionDOMapper;
    @Resource
    private RolePermissionDOMapper rolePermissionDOMapper;

    private static final String PUSH_PERMISSION_FLAG = "push.permission.flag";

    @Override
    public void run(ApplicationArguments args) {
        log.info("服务启动====>开始将角色权限信息存入redis中");
        //TODO:将角色权限信息推送到redis中

        try {
            // 是否能够同步数据: 原子操作，只有在键 PUSH_PERMISSION_FLAG 不存在时，才会设置该键的值为 "1"，并设置过期时间为 1 天
            Boolean canPushed = redisTemplate.opsForValue().setIfAbsent(PUSH_PERMISSION_FLAG, "1", 1, TimeUnit.DAYS);
            if (Boolean.FALSE.equals(canPushed)) {
                log.warn("角色权限信息已经推送过，无需重复推送");
                return;
            }
            //查询所有角色
            List<RoleDO> roleDOS = roleDOMapper.selectEnabledList();
            if (CollUtil.isNotEmpty(roleDOS)) {
                List<Long> ids = roleDOS.stream().map(RoleDO::getId).toList();
                //根据角色ID，批量查询所有角色的权限
                List<RolePermissionDO> rolePermissionDoS = rolePermissionDOMapper.selectByRoleIds(ids);
                //按角色ID分组，每个角色ID对应多个权限ID
                Map<Long, List<Long>> roleIdPermissionIdsMap = rolePermissionDoS.stream()
                        .collect(Collectors.groupingBy(RolePermissionDO::getRoleId,
                                Collectors.mapping(RolePermissionDO::getPermissionId, Collectors.toList())));

                //查询APP端所有启用的权限
                List<PermissionDO> permissionDoS = permissionDOMapper.selectAppEnabledList();
                Map<Long, PermissionDO> permissionIdDoMap = permissionDoS.stream().collect(Collectors.toMap(PermissionDO::getId, permissionDO -> permissionDO));

                //组织 角色ID-权限集合的关系
                Map<Long, List<PermissionDO>> roleIdPermissionDoMap = Maps.newHashMap();

                roleDOS.forEach(roleDO -> {
                    List<Long> permissionIds = roleIdPermissionIdsMap.get(roleDO.getId());
                    if (CollUtil.isNotEmpty(permissionIds)) {
                        List<PermissionDO> perDos = Lists.newArrayList();
                        permissionIds.forEach(permissionId -> {
                            PermissionDO permissionDO = permissionIdDoMap.get(permissionId);
                            perDos.add(permissionDO);
                        });
                        roleIdPermissionDoMap.put(roleDO.getId(), perDos);
                    }
                });
                //同步至redis中，方便后续网关查询鉴权使用
                roleIdPermissionDoMap.forEach((roleId, permissionDOS) -> {
                    String key = RedisKeyConstants.buildRolePermissionsKey(roleId);
                    redisTemplate.opsForValue().set(key, JsonUtils.toJsonString(permissionDOS));
                });
                log.info("服务启动====>完成将角色权限信息存入redis中");
            }
        } catch (Exception e) {
            log.error("服务启动====>将角色权限信息存入redis中失败", e);
        }
    }
}